#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace TankWars
{
    /// <summary>
    /// This screen implements the network game logic.
    /// </summary>
    class NetworkGameplayScreen : GameScreen
    {
        #region Fields

        #region Network Variables
        NetworkSession networkSession;

        List<Player> allPlayers;

        PacketWriter packetWriter = new PacketWriter();
        PacketReader packetReader = new PacketReader();
        const int TIME_BETWEEN_PACKETS = 15;
        int lastPacketSentTime = 0;

        AudioEngine AEngine;
        WaveBank tankWavs;
        SoundBank tankSounds;
        Cue primshot;
        Cue anim;

        private enum WeaponEnum { Bullet, Laser, Mine, Missile, PlasmaBall };
        #endregion

        #region Graphic Variables
        ContentManager content;
        SpriteBatch spriteBatch;

        private SpriteFont font;
        private Texture2D statusAreaBorderTexture;

        private Texture2D TankTexture;
        private Dictionary<Type, Texture2D> dicShotTextures = new Dictionary<Type, Texture2D>();
        private Dictionary<Type, Texture2D> dicShotIcons = new Dictionary<Type, Texture2D>();
        Dictionary<Type, string> dicSpriteNames = new Dictionary<Type, string>();
        private Texture2D[] aryMapTextures;

        private string strAnimationXMLFile = @"..\..\..\Animations.xml";
        private string strMapXMLFile = @"..\..\..\Maps\Level4.xml";

        //Field textures.
        //private Texture2D blockTexture;

        private Texture2D mapTexture;
        #endregion

        #region Game Variables
        private Player mainPlayer;

        private Rules gameRules;

        private TankManager tankManager;
        private GameField field;

        //private Texture2D mousePointer;
        private Vector2 mousePosition;
        private MouseState mouseState;

        private Map gameMap;

        // A viewport that is the main window, and two sub-viewports for the battlefield and the status area
        private Viewport defaultViewport;
        private Viewport battlefieldViewport;
        private StatusArea statusArea;
        #endregion

        #endregion

        #region Properties


        /// <summary>
        /// The logic for deciding whether the game is paused depends on whether
        /// this is a networked or single player game. If we are in a network session,
        /// we should go on updating the game even when the user tabs away from us or
        /// brings up the pause menu, because even though the local player is not
        /// responding to input, other remote players may not be paused. In single
        /// player modes, however, we want everything to pause if the game loses focus.
        /// </summary>
        new bool IsActive
        {
            get
            {
                if (networkSession == null)
                {
                    // Pause behavior for single player games.
                    return base.IsActive;
                }
                else
                {
                    // Pause behavior for networked games.
                    return !IsExiting;
                }
            }
        }

        #endregion


        #region Initialization and Loading
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>

        /// <summary>
        /// Constructor.
        /// </summary>
        public NetworkGameplayScreen(NetworkSession networkSession)
        {
            // Handles transitions between screens
            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);

            AEngine = new AudioEngine("Content\\Audio\\TWAudio.xgs");
            tankSounds = new SoundBank(AEngine, "Content\\Audio\\Sound Bank.xsb");
            tankWavs = new WaveBank(AEngine, "Content\\Audio\\Wave Bank.xwb");
            primshot = tankSounds.GetCue("Missile");
            anim = tankSounds.GetCue("Impact");

            // Transfer network session from previous screen
            this.networkSession = networkSession;

            // Create a generic collection of Player objects
            allPlayers = new List<Player>(this.networkSession.AllGamers.Count);
            // Assign each of them gamer tags
            for(int i = 0; i < this.networkSession.AllGamers.Count; i++)
            {
                NetworkGamer gamer = this.networkSession.AllGamers[i];

                Player newPlayer = new Player(gamer.Gamertag);
                allPlayers.Add(newPlayer);
                gamer.Tag = newPlayer; 
            }
        }


        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            // Create a content manager that can be disposed of when not in the game
            if (content == null)
            {
                content = new ContentManager(ScreenManager.Game.Services, "Content");
            }
  

            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(ScreenManager.GraphicsDevice);

            //Load sprite fonts
            this.font = content.Load<SpriteFont>("Fonts/RockwellFont");

            #region Load all textures
            this.statusAreaBorderTexture = content.Load<Texture2D>("StatusAreaBorder");

            this.TankTexture = content.Load<Texture2D>("Tanks/tank2base");

            //Load all map textures
            aryMapTextures = new Texture2D[1];
            aryMapTextures[0] = content.Load<Texture2D>("MapObjects\\obj1");
            field = new GameField(aryMapTextures, this.spriteBatch);

            string strBulletSpriteName = "BulletAnimation";
            string strPlasmaBallSpriteName = "PlasmaAnimation";
            string strLaserSpriteName = "LaserAnimation";
            string strMissileSpriteName = "MissileAnimation";
            string strMineSpriteName = "MineAnimation";
            Dictionary<Type, string> dicSpriteNames = new Dictionary<Type, string>();

            //Load all shot textures
            this.dicShotTextures.Add(typeof(Weapons.Bullet), content.Load<Texture2D>("Weapons/" + strBulletSpriteName));
            this.dicSpriteNames.Add(typeof(Weapons.Bullet), strBulletSpriteName);
            this.dicShotTextures.Add(typeof(Weapons.PlasmaBall), content.Load<Texture2D>("Weapons/" + strPlasmaBallSpriteName));
            this.dicSpriteNames.Add(typeof(Weapons.PlasmaBall), strPlasmaBallSpriteName);
            this.dicShotTextures.Add(typeof(Weapons.Laser), content.Load<Texture2D>("Weapons/" + strLaserSpriteName));
            this.dicSpriteNames.Add(typeof(Weapons.Laser), strLaserSpriteName);
            this.dicShotTextures.Add(typeof(Weapons.Missile), content.Load<Texture2D>("Weapons/" + strMissileSpriteName));
            this.dicSpriteNames.Add(typeof(Weapons.Missile), strMissileSpriteName);
            this.dicShotTextures.Add(typeof(Weapons.Mine), content.Load<Texture2D>("Weapons/" + strMineSpriteName));
            this.dicSpriteNames.Add(typeof(Weapons.Mine), strMineSpriteName);

            //Load all shot icons
            this.dicShotIcons.Add(typeof(Weapons.Bullet), content.Load<Texture2D>("Weapons/Bullet"));
            this.dicShotIcons.Add(typeof(Weapons.PlasmaBall), content.Load<Texture2D>("Weapons/PlasmaBall"));
            this.dicShotIcons.Add(typeof(Weapons.Laser), content.Load<Texture2D>("Weapons/Laser"));
            this.dicShotIcons.Add(typeof(Weapons.Missile), content.Load<Texture2D>("Weapons/Missile"));
            this.dicShotIcons.Add(typeof(Weapons.Mine), content.Load<Texture2D>("Weapons/Mine"));

            String mapName = "Maps\\Level4";
            this.mapTexture = content.Load<Texture2D>(mapName);
            field.setUpLevel(this.strMapXMLFile, mapTexture.Width, mapTexture.Height);
            #endregion

            foreach (NetworkGamer gamer in this.networkSession.AllGamers)
            {
                if (gamer.IsLocal)
                {
                    this.mainPlayer = (Player)gamer.Tag;
                }
                ((Player)gamer.Tag).setTank(new Tank(this.TankTexture, this.content.Load<Texture2D>("Tanks/tank2turret"), (Player)gamer.Tag, this.dicShotTextures, this.dicShotIcons, this.dicSpriteNames, this.strAnimationXMLFile));
                ((Player)gamer.Tag).Tank.vPosition = field.getSpawnPoint();
            }

            this.tankManager = new TankManager(this.TankTexture, this.content.Load<Texture2D>("Tanks/tank2turret"), this.dicShotTextures, this.dicShotIcons, this.dicSpriteNames, this.strAnimationXMLFile, this.spriteBatch);

            

            // Set battlefield viewport to current screen dimensions, create a status area,
            //  and resize battlefield dimensions to reflect status area size, saving full screen
            //  in a default viewport
            this.defaultViewport = ScreenManager.GraphicsDevice.Viewport;
            this.battlefieldViewport = this.defaultViewport;
            this.statusArea = new StatusArea(this.statusAreaBorderTexture, this.font, mainPlayer.Tank, this.defaultViewport);
            this.battlefieldViewport = this.statusArea.ResizeViewport(defaultViewport);

            //this.mousePointer = Content.Load<Texture2D>("Mouse");

            this.gameMap = new Map(this.mapTexture, mainPlayer.Tank, battlefieldViewport);

            List<Player> lstPlayers = new List<Player>();
            foreach (NetworkGamer gamer in this.networkSession.AllGamers)
            {
                lstPlayers.Add((Player)gamer.Tag);
            }
            this.gameRules = new LastManStandingRules(lstPlayers, 2);

            this.tankManager.gameRules = this.gameRules;

            // Assign tank manager to current network session and hook session events
            tankManager.setNetwork(networkSession);
            HookSessionEvents();
        }


        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            content.Unload();
        }

        #endregion

        #region Update

        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                       bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

            if (IsActive)
            {
                // if something else has canceled our game, then exit
                if (networkSession == null && !this.gameRules.IsGameEnded)
                {
                    if (!IsExiting)
                    {
                        ExitScreen();
                    }
                    base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
                    return;
                }

                // As long as the game is not ended, update game session
                if (!this.gameRules.IsGameEnded)
                {
                    //  If you are fully dead, show an overlay
                    if (mainPlayer.Tank.IsDead && mainPlayer.Lives == 0)
                    {
                        //ScreenManager.AddScreen(new PauseMenuScreen(networkSession));
                        ScreenManager.AddScreen(new StaticStatsScreen(allPlayers, "You're Dead - Current Stats", gameRules));
                    }

                    // When covered by another screen, still update
                    updateGameSession(gameTime);
                    this.gameRules.Update(gameTime);
                }
                else
                {
                    // If game is ended, bring up the final stats screen
                    StatsScreen finalStats = new StatsScreen(allPlayers, "Game Over");
                    finalStats.Cleared += ConfirmLeaveGameAccepted;
                    ScreenManager.AddScreen(finalStats);
                }

                // Handler for when "return to lobby" is selected from pause menu
                if ((networkSession != null) && !IsExiting)
                {
                    if (networkSession.SessionState == NetworkSessionState.Lobby)
                    {
                        LoadingScreen.Load(ScreenManager, false, new BackgroundScreen(), new LobbyScreen(networkSession));
                    }
                }
            }
        }


        /// <summary>
        /// Lets the game pause menu respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            if (input.PauseGame)
            {
                // If they pressed pause, bring up the pause menu screen.
                ScreenManager.AddScreen(new PauseMenuScreen(networkSession));
            }

            Player localPlayer;

            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {
                // Assign local player by gamer tag
                localPlayer = gamer.Tag as Player;

                // Always clear input before searching for it
                localPlayer.Tank.clearInput();

                #region Movement Input

                // Check for local gamepad tank movement update
                if (input.GoLeft)
                    localPlayer.Tank.leftInput = true;
                if (input.GoRight)
                    localPlayer.Tank.rightInput = true;
                if (input.GoUp)
                    localPlayer.Tank.upInput = true;
                if (input.GoDown)
                    localPlayer.Tank.downInput = true;

                #endregion

                #region Fire Input

                // Check for local gamepad tank primary and secondary firing
                if (input.FireMainWeapon)
                    localPlayer.Tank.primaryInput = true;
                if (input.FireSecondaryWeapon)
                    localPlayer.Tank.secondaryInput = true;

                #endregion

                #region Mouse Movement Restriction

                this.mousePosition = input.CurrentMouseCoords;

                // Restrict the Mouse so that it stays inside the current display
                if (this.mousePosition.X < 0)
                    this.mousePosition.X = 0;
                if (this.mousePosition.X > ScreenManager.GraphicsDevice.Viewport.Width)
                    this.mousePosition.X = ScreenManager.GraphicsDevice.Viewport.Width;
                if (this.mousePosition.Y < 0)
                    this.mousePosition.Y = 0;
                if (this.mousePosition.Y > ScreenManager.GraphicsDevice.Viewport.Height)
                    this.mousePosition.Y = ScreenManager.GraphicsDevice.Viewport.Height;

                #endregion
            }
        }

        /// <summary>
        /// Once the player exits the final statistics screen, the game ends and returns
        ///  to the main menu, removing the network session.
        /// </summary>
        void ConfirmLeaveGameAccepted(object sender, EventArgs e)
        {            
            // Return to the main menu.
            LoadingScreen.Load(ScreenManager, false, new BackgroundScreen(),new MainMenuScreen());
            // Remove the network session
            networkSession = null;
        }

        /// <summary>
        /// Updates the mechanics of the game.
        /// </summary>
        public void updateGameSession(GameTime gameTime)
        {
            // Read input for local players, and sends them to the server.
            //If this is the server we just read local player's input.
            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {//For loop not needed since the current version can have only 1 local gamer.
                sendPacketsToServer(gamer, gameTime);
            }

            // If we are the server, update all the tanks and transmit
            // their latest positions back out over the network.
            if (networkSession.IsHost)
            {
                sendPacketsToClient(gameTime);
            }

            // Pump the underlying session object.
            networkSession.Update();

            // Make sure the session has not ended.
            if (networkSession == null)
                return;

            //Read packets for all gamers.
            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {
                if (gamer.IsHost)
                {
                    receivePacketsFromClients(gamer);
                }
                else
                {
                    receivePacketsFromServer(gamer,gameTime);
                }
            }
        }

        #endregion

        #region Network Functions

        /// NOTE: The following functions are located in "NetworkSelectionScreen.cs":
        /// CreateSession
        /// JoinSession

        /// <summary>
        /// After creating or joining a network session, we must subscribe to
        /// some events so we will be notified when the session changes state.
        /// </summary>
        void HookSessionEvents()
        {
            //networkSession.GamerJoined += GamerJoinedEventHandler;
            networkSession.SessionEnded += SessionEndedEventHandler;
        }

        /*
        /// <summary>
        /// This event handler will be called whenever a new gamer joins the session.
        /// We use it to allocate a Tank object, and associate it with the new gamer.
        /// </summary>
        void GamerJoinedEventHandler(object sender, GamerJoinedEventArgs e)
        {
            int gamerIndex = networkSession.AllGamers.IndexOf(e.Gamer);

            if (e.Gamer.IsLocal)
            {
                e.Gamer.Tag = this.mainPlayer;
                this.mainPlayer.Tank.vPosition = field.getSpawnPoint();
            }
            else//Create a new tank if it is not local
            {
                //Create a new player.
                Player joiningPlayer = new Player("Player#" + gamerIndex);
                this.tankManager.NewTank(joiningPlayer);
                joiningPlayer.Tank.vPosition = field.getSpawnPoint();
                e.Gamer.Tag = joiningPlayer;
            }
        }

         */
        /// <summary>
        /// Event handler notifies us when the network session has ended and thus the game is ended (prematurely).
        /// </summary>
        void SessionEndedEventHandler(object sender, NetworkSessionEndedEventArgs e)
        {
            if (networkSession.IsHost)
            {
                networkSession.EndGame();
            }
        }

        public void sendPacketsToClient(GameTime gameTime)
        {
            //Update the tanks on the server.
            this.tankManager.Update(gameTime, Mouse.GetState(), this.gameMap,field, this.mainPlayer);
            this.mainPlayer.Tank.updateEngineSounds();

            //Now send the update to the clients, provided there is still a network session.
            if (networkSession != null)
            {
                if ((gameTime.TotalGameTime.Milliseconds - this.lastPacketSentTime >= TIME_BETWEEN_PACKETS) || (gameTime.TotalGameTime.Milliseconds < this.lastPacketSentTime))
                {
                    this.lastPacketSentTime = gameTime.TotalGameTime.Milliseconds;

                    // First off, our packet will indicate how many tanks it has data for.
                    packetWriter.Write(networkSession.AllGamers.Count);

                    // Loop over all the players in the session, not just the local ones!
                    foreach (NetworkGamer gamer in networkSession.AllGamers)
                    {
                        // Look up what tank is associated with this player.
                        Player anyPlayer = gamer.Tag as Player;

                        // Write the tank state into the output network packet.
                        packetWriter.Write(anyPlayer.Tank.vPosition);
                        packetWriter.Write(anyPlayer.Tank.fRotation);
                        packetWriter.Write(anyPlayer.Tank.Turret.fRotation);
                        packetWriter.Write(anyPlayer.Tank.Health);
                        packetWriter.Write(anyPlayer.Tank.IsRespawning);
                        packetWriter.Write(anyPlayer.Tank.IsDead);
                        packetWriter.Write(anyPlayer.Lives);
                        packetWriter.Write(anyPlayer.Kills);

                        int endBit = -1;
                        //Primary weapons
                        foreach (Weapons.Shot currentShot in anyPlayer.Tank.WeaponManager.PrimaryShotsFired)
                        {
                            int missileType = (int)WeaponEnum.Bullet;
                            if (currentShot.GetType() == typeof(Weapons.Bullet))
                            {
                                missileType = (int)WeaponEnum.Bullet;
                            }
                            else if (currentShot.GetType() == typeof(Weapons.Laser))
                            {
                                missileType = (int)WeaponEnum.Laser;
                            }
                            else if (currentShot.GetType() == typeof(Weapons.Mine))
                            {
                                missileType = (int)WeaponEnum.Mine;
                            }
                            else if (currentShot.GetType() == typeof(Weapons.Missile))
                            {
                                missileType = (int)WeaponEnum.Missile;
                            }
                            else if (currentShot.GetType() == typeof(Weapons.PlasmaBall))
                            {
                                missileType = (int)WeaponEnum.PlasmaBall;
                            }
                            packetWriter.Write(missileType);
                            packetWriter.Write(currentShot.vPosition.X);
                            packetWriter.Write(currentShot.vPosition.Y);
                            packetWriter.Write(currentShot.fRotation);
                            packetWriter.Write(currentShot.bIsExploded);
                        }
                        packetWriter.Write(endBit);

                        //Secondary weapons.
                        foreach (Weapons.Shot currentShot in anyPlayer.Tank.WeaponManager.SecondaryShotsFired)
                        {
                            int missileType = (int)WeaponEnum.Bullet;
                            if (currentShot.GetType() == typeof(Weapons.Bullet))
                            {
                                missileType = (int)WeaponEnum.Bullet;
                            }
                            else if (currentShot.GetType() == typeof(Weapons.Laser))
                            {
                                missileType = (int)WeaponEnum.Laser;
                            }
                            else if (currentShot.GetType() == typeof(Weapons.Mine))
                            {
                                missileType = (int)WeaponEnum.Mine;
                            }
                            else if (currentShot.GetType() == typeof(Weapons.Missile))
                            {
                                missileType = (int)WeaponEnum.Missile;
                            }
                            else if (currentShot.GetType() == typeof(Weapons.PlasmaBall))
                            {
                                missileType = (int)WeaponEnum.PlasmaBall;
                            }
                            packetWriter.Write(missileType);
                            packetWriter.Write(currentShot.vPosition.X);
                            packetWriter.Write(currentShot.vPosition.Y);
                            packetWriter.Write(currentShot.fRotation);
                            packetWriter.Write(currentShot.bIsExploded);
                        }
                        packetWriter.Write(endBit);
                    }
                    // Send the combined data for all tanks to everyone in the session.
                    LocalNetworkGamer server = (LocalNetworkGamer)networkSession.Host;
                    server.SendData(packetWriter, SendDataOptions.InOrder);
                }//If TIME_BETWEEN_PACKETS
            }//If network session null
        }

        ///<summary>
        /// Function called by client to send data to the server.
        /// </summary>
        public void sendPacketsToServer(LocalNetworkGamer gamer, GameTime gameTime)
        {
            //The passed in parameter is a "local gamer".  Therefore
            //look up the player associated with this gamer and get
            //the turret input.
            Player localPlayer = gamer.Tag as Player;            

            localPlayer.Tank.Turret.UpdatePosition(Mouse.GetState(), this.gameMap, gameTime);
            //localPlayer.Tank.Turret.UpdatePosition(null, input.rightStick(), this.gameMap, gameTime);

            // Assure there is still a network session
            if (networkSession != null)
            {
                // Only send if we are not the server. There is no point sending packets
                // to ourselves, because we already know what they will contain!
                if (!networkSession.IsHost)
                {
                    if ((gameTime.TotalGameTime.Milliseconds - this.lastPacketSentTime >= TIME_BETWEEN_PACKETS) || (gameTime.TotalGameTime.Milliseconds < this.lastPacketSentTime))
                    {
                        this.lastPacketSentTime = gameTime.TotalGameTime.Milliseconds;

                        // Write our latest input state into a network packet.
                        packetWriter.Write(localPlayer.Tank.leftInput);
                        packetWriter.Write(localPlayer.Tank.rightInput);
                        packetWriter.Write(localPlayer.Tank.upInput);
                        packetWriter.Write(localPlayer.Tank.downInput);
                        packetWriter.Write(localPlayer.Tank.primaryInput);
                        packetWriter.Write(localPlayer.Tank.secondaryInput);
                        packetWriter.Write(localPlayer.Tank.Turret.fRotation);

                        // Send our input data to the server.
                        gamer.SendData(packetWriter, SendDataOptions.InOrder, networkSession.Host);
                    }
                }
            }
        }

        ///<summary>
        ///Function called by server to receive packets from clients.
        ///</summary>
        public void receivePacketsFromClients(LocalNetworkGamer hostGamer)
        {
            // Keep reading as long as incoming packets are available.
            while (hostGamer.IsDataAvailable)
            {
                if (networkSession != null)
                {
                    NetworkGamer sender;

                    // Read a single packet from the network.
                    hostGamer.ReceiveData(packetReader, out sender);

                    if (!sender.IsLocal)
                    {
                        // Look up the tank associated with whoever sent this packet.
                        Player remotePlayer = sender.Tag as Player;

                        // Read the latest inputs controlling this tank.
                        remotePlayer.Tank.leftInput = packetReader.ReadBoolean();
                        remotePlayer.Tank.rightInput = packetReader.ReadBoolean();
                        remotePlayer.Tank.upInput = packetReader.ReadBoolean();
                        remotePlayer.Tank.downInput = packetReader.ReadBoolean();
                        remotePlayer.Tank.primaryInput = packetReader.ReadBoolean();
                        remotePlayer.Tank.secondaryInput = packetReader.ReadBoolean();
                        remotePlayer.Tank.Turret.fRotation = packetReader.ReadSingle();
                    }
                }
            }
        }

        /// <summary>
        /// Function called by client to recieve data from server.
        /// </summary>
        public void receivePacketsFromServer(LocalNetworkGamer gamer,GameTime gameTime)
        {
            this.mainPlayer.Tank.updateEngineSounds();

            if (networkSession != null)
            {
                // This packet contains data about all the players in the session.
                foreach (NetworkGamer remoteGamer in networkSession.AllGamers)
                {
                    Player remotePlayer = remoteGamer.Tag as Player;

                    //Update primary missile animations.
                    for (int index = 0; index < remotePlayer.Tank.WeaponManager.PrimaryShotsFired.Count; index++)
                    {
                        if (remotePlayer.Tank.WeaponManager.PrimaryShotsFired[index].IsAnimating)
                            remotePlayer.Tank.WeaponManager.PrimaryShotsFired[index].ContinueAnimation(gameTime);

                        if (remotePlayer.Tank.WeaponManager.PrimaryShotsFired[index].bIsExploded)
                            if (remotePlayer.Tank.WeaponManager.PrimaryShotsFired[index].IsAnimating == false)
                                remotePlayer.Tank.WeaponManager.RemovePrimaryShot(index);
                    }

                    //Update secondary missile animations.
                    for (int index = 0; index < remotePlayer.Tank.WeaponManager.SecondaryShotsFired.Count; index++)
                    {
                        if (remotePlayer.Tank.WeaponManager.SecondaryShotsFired[index].IsAnimating)
                            remotePlayer.Tank.WeaponManager.SecondaryShotsFired[index].ContinueAnimation(gameTime);

                        if (remotePlayer.Tank.WeaponManager.SecondaryShotsFired[index].bIsExploded)
                            if (remotePlayer.Tank.WeaponManager.SecondaryShotsFired[index].IsAnimating == false)
                                remotePlayer.Tank.WeaponManager.RemoveSecondaryShot(index);
                    }
                }

                // Keep reading as long as incoming packets are available.
                while (gamer.IsDataAvailable)
                {
                    NetworkGamer sender;

                    // Read a single packet from the network.
                    gamer.ReceiveData(packetReader, out sender);

                    // If a player has recently joined or left, it is possible the server
                    // might have sent information about a different number of players
                    // than the client currently knows about. If so, we will be unable
                    // to match up which data refers to which player. The solution is
                    // just to ignore the packet for now: this situation will resolve
                    // itself as soon as the client gets the join/leave notification.
                    if (networkSession.AllGamers.Count != packetReader.ReadInt32())
                        continue;

                    // This packet contains data about all the players in the session.
                    foreach (NetworkGamer remoteGamer in networkSession.AllGamers)
                    {
                        Player remotePlayer = remoteGamer.Tag as Player;

                        // Read the state of this tank from the network packet.
                        remotePlayer.Tank.vPosition = packetReader.ReadVector2();
                        remotePlayer.Tank.fRotation = packetReader.ReadSingle();
                        float temp = packetReader.ReadSingle();
                        if (!remoteGamer.IsLocal)
                        {
                            remotePlayer.Tank.Turret.fRotation = temp;
                        }
                        remotePlayer.Tank.Health = packetReader.ReadInt32();
                        remotePlayer.Tank.IsRespawning = packetReader.ReadBoolean();
                        remotePlayer.Tank.IsDead = packetReader.ReadBoolean();
                        remotePlayer.Lives = packetReader.ReadInt32();
                        remotePlayer.Kills = packetReader.ReadInt32();

                        int missleNumber = 1;
                        while (true)
                        {

                            int packet = packetReader.ReadInt32();
                            if (packet == -1)
                            {
                                break;
                            }
                            else
                            {
                                int Type = packet;
                                float mX = packetReader.ReadSingle();
                                float mY = packetReader.ReadSingle();
                                float mR = packetReader.ReadSingle();
                                bool mE = packetReader.ReadBoolean();

                                if (missleNumber > remotePlayer.Tank.WeaponManager.PrimaryShotsFired.Count)
                                {
                                    Type newShotType = typeof(Weapons.Bullet);
                                    if (Type == (int)WeaponEnum.Bullet)
                                    {
                                        newShotType = typeof(Weapons.Bullet);
                                    }
                                    else if (Type == (int)WeaponEnum.Laser)
                                    {
                                        newShotType = typeof(Weapons.Laser);
                                    }
                                    else if (Type == (int)WeaponEnum.Mine)
                                    {
                                        newShotType = typeof(Weapons.Mine);
                                    }
                                    else if (Type == (int)WeaponEnum.Missile)
                                    {
                                        newShotType = typeof(Weapons.Missile);
                                    }
                                    else if (Type == (int)WeaponEnum.PlasmaBall)
                                    {
                                        newShotType = typeof(Weapons.PlasmaBall);
                                    }

                                    remotePlayer.Tank.WeaponManager.AddPrimaryShot(newShotType, new Vector2(mX, mY), mR, mE);
                                    if (remoteGamer.IsLocal)
                                    {
                                        primshot = tankSounds.GetCue("Missile");
                                        primshot.Play();
                                    }
                                }

                                if (remotePlayer.Tank.WeaponManager.PrimaryShotsFired[missleNumber - 1].bIsExploded == false)
                                {
                                    remotePlayer.Tank.WeaponManager.UpdatePrimaryShot(missleNumber - 1, new Vector2(mX, mY), mR, mE);
                                    if (mE == true)
                                    {
                                        string explodeLabel = remotePlayer.Tank.WeaponManager.PrimaryShotsFired[missleNumber - 1].ExplodeAnimationLabel;
                                        remotePlayer.Tank.WeaponManager.PrimaryShotsFired[missleNumber - 1].StartAnimation(gameTime, explodeLabel);
                                        float distance = ((float)mX - mainPlayer.Tank.vPosition.X) * ((float)mY - mainPlayer.Tank.vPosition.Y) + (mY - mainPlayer.Tank.vPosition.Y) * (mY - mainPlayer.Tank.vPosition.Y);
                                        if (Math.Sqrt((double)distance) < 400)
                                        {
                                            primshot.Stop(AudioStopOptions.Immediate);
                                            if (anim.IsPlaying)
                                            { }
                                            else
                                            {
                                                anim = tankSounds.GetCue("Impact");
                                                anim.Play();
                                            }
                                        }
                                    }

                                }
                                missleNumber++;
                            }
                        }//While (true)

                        missleNumber = 1;
                        while (true)
                        {

                            int packet = packetReader.ReadInt32();
                            if (packet == -1)
                            {
                                break;
                            }
                            else
                            {
                                int Type = packet;
                                float mX = packetReader.ReadSingle();
                                float mY = packetReader.ReadSingle();
                                float mR = packetReader.ReadSingle();
                                bool mE = packetReader.ReadBoolean();

                                if (missleNumber > remotePlayer.Tank.WeaponManager.SecondaryShotsFired.Count)
                                {
                                    Type newShotType = typeof(Weapons.Bullet);
                                    if (Type == (int)WeaponEnum.Bullet)
                                    {
                                        newShotType = typeof(Weapons.Bullet);
                                    }
                                    else if (Type == (int)WeaponEnum.Laser)
                                    {
                                        newShotType = typeof(Weapons.Laser);
                                    }
                                    else if (Type == (int)WeaponEnum.Mine)
                                    {
                                        newShotType = typeof(Weapons.Mine);
                                    }
                                    else if (Type == (int)WeaponEnum.Missile)
                                    {
                                        newShotType = typeof(Weapons.Missile);
                                    }
                                    else if (Type == (int)WeaponEnum.PlasmaBall)
                                    {
                                        newShotType = typeof(Weapons.PlasmaBall);
                                    }

                                    remotePlayer.Tank.WeaponManager.AddSecondaryShot(newShotType, new Vector2(mX, mY), mR, mE);
                                }

                                if (remotePlayer.Tank.WeaponManager.SecondaryShotsFired[missleNumber - 1].bIsExploded == false)
                                {
                                    remotePlayer.Tank.WeaponManager.UpdateSecondaryShot(missleNumber - 1, new Vector2(mX, mY), mR, mE);
                                    if (mE == true)
                                    {
                                        string explodeLabel = remotePlayer.Tank.WeaponManager.SecondaryShotsFired[missleNumber - 1].ExplodeAnimationLabel;
                                        remotePlayer.Tank.WeaponManager.SecondaryShotsFired[missleNumber - 1].StartAnimation(gameTime, explodeLabel);
                                        primshot.Stop(AudioStopOptions.Immediate);
                                        if (anim.IsPlaying)
                                        { }
                                        else
                                        {
                                            anim = tankSounds.GetCue("Impact");
                                            anim.Play();
                                        }
                                    }

                                }
                                missleNumber++;
                            }
                        }//While (true)
                    }
                }
            }
        }
        #endregion

        #region Draw

        /// <summary>
        /// Draws the gameplay screen, along with all its tanks, projectiles, map, and static objects.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            //Do not draw game images until the network has been initialized.
            if (networkSession == null)
                return;

            //Clear screen.
            //ScreenManager.GraphicsDevice.Clear(Color.CornflowerBlue);

            //Always draw map first
            this.gameMap.Draw(this.spriteBatch);

            this.tankManager.Draw(this.gameMap);
            this.gameRules.Draw(this.spriteBatch, this.gameMap);
            this.field.drawField(this.mainPlayer, this.gameMap);
            this.statusArea.Draw(this.spriteBatch, ScreenManager.GraphicsDevice);

            base.Draw(gameTime);
            //this.spriteBatch.Begin(SpriteBlendMode.Additive);
            //this.spriteBatch.Draw(mousePointer, mousePosition, Color.White);
            //this.spriteBatch.End();

            // If the game is transitioning on or off, fade it out to black.
            if (TransitionPosition > 0)
                ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
        }

        /// <summary>
        /// Helper draws notification messages before calling blocking network methods.
        /// </summary>
        void DrawMessage(string message)
        {
            //if (!BeginDraw())
            //    return;

            ScreenManager.GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();

            spriteBatch.DrawString(font, message, new Vector2(161, 161), Color.Black);
            spriteBatch.DrawString(font, message, new Vector2(160, 160), Color.White);

            spriteBatch.End();

            //EndDraw();
        }
        #endregion        
    }
}